﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using PulseLibrary;
using PulsePrototype.Messages.DescriptionMessages;
using FarseerPhysics.Dynamics.Joints;
using PulsePrototype.Messages;
using PulsePrototype.Messages.PhysicsMessages;
using PulsePrototype.Messages.PositionMessages;

namespace PulsePrototype.PulseManagers.Components
{
    public class PhysicsComponent : BaseComponent
    {
        private Body mainBody;
        private Body wheelBody;
        private RevoluteJoint axis;
        private float speed;
        private List<Body> bodyLimbs = new List<Body>();

        private Vector2 size;
        public const float unitToPixel = 100.0f;
        public const float pixelToUnit = 1 / unitToPixel;

        public Body MainBody
        {
            get { return this.mainBody; }
        }

        public List<Body> BodyLimbs
        {
            get { return bodyLimbs; }
            set { bodyLimbs = value; }
        }

        public Vector2 Size
        {
            get { return size * unitToPixel; }
            set { size = value * pixelToUnit; }
        }

        protected override string GetName()
        {
            return "Physics";
        }

        public PhysicsComponent(BaseEntity ParentEntity, World world, Vector2 siz, Vector2 pos) :
            base(ParentEntity)
        {
            this.mainBody = BodyFactory.CreateRectangle(world,
                ConvertUnits.ToSimUnits(siz.X),
                ConvertUnits.ToSimUnits(siz.Y),
                1, pos);

            this.mainBody.BodyType = BodyType.Dynamic;
            this.Size = siz;
            Initialize();
        }

        /// <summary>
        /// Create a PhysicsComponent with a torso and a wheel for a player character.
        /// </summary>
        public PhysicsComponent(BaseEntity ParentEntity, World world, Vector2 siz, Vector2 pos, float speed) :
            base(ParentEntity)
        {
            // Create the main torso body.
            Vector2 torsoSize = new Vector2(siz.X, siz.Y - siz.X / 2.0f);

            this.mainBody = BodyFactory.CreateRectangle(world,
                ConvertUnits.ToSimUnits(torsoSize.X),
                ConvertUnits.ToSimUnits(torsoSize.Y),
                1, pos);

            this.mainBody.FixedRotation = true;
            this.mainBody.BodyType = BodyType.Dynamic;

            // Create the wheel to drive on and add it to the body part list.
            float radius = siz.X / 2.0f;
            wheelBody = BodyFactory.CreateCircle(world, ConvertUnits.ToSimUnits(radius), 1.0f);
            wheelBody.Position = mainBody.Position + ConvertUnits.ToSimUnits(new Vector2(0, torsoSize.Y / 2.0f));
            wheelBody.Friction = 1.0f;
            wheelBody.BodyType = BodyType.Dynamic;
            
            // Attach the wheel and torso together.
            axis = JointFactory.CreateRevoluteJoint(world, mainBody, wheelBody, Vector2.Zero);
            axis.CollideConnected = false;

            this.speed = speed;

            //axis.MotorSpeed = speed * MathHelper.Pi;
            axis.MotorImpulse = 100;
            axis.MotorEnabled = true;
            axis.MaxMotorTorque = 100;

            this.size = siz;
            Initialize();
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected void AddBodyPart(Body bodyPart)
        {
            bodyLimbs.Add(bodyPart);
        }

        public override bool SendMessage(BaseMessage msg)
        {
            // Entity has a switch for any messages it cares about
            switch (msg.messageType)
            {
                case MessageType.SetWheelSpeed:
                    {
                        MsgSetWheelSpeed msgSetSpeed = msg as MsgSetWheelSpeed;
                        axis.MotorSpeed = msgSetSpeed.speed * speed * MathHelper.Pi;

                        // Console.WriteLine("RenderComponent handling SetPosition");
                    }
                    break;
                case MessageType.GetWheelSpeed:
                    {
                        MsgSetWheelSpeed msgSetSpeed = msg as MsgSetWheelSpeed;
                        msgSetSpeed.speed = axis.MotorSpeed / speed / MathHelper.Pi;
                    }
                    break;
                case MessageType.SetVelocity:
                    {
                        MsgSetVelocity msgSetVelocity = msg as MsgSetVelocity;
                        Vector2 force = new Vector2(msgSetVelocity.x, msgSetVelocity.y);
                        mainBody.ApplyForce(force);
                    }
                    break;
                case MessageType.GetVelocity:
                    {
                        MsgGetVelocity msgGetVelocity = msg as MsgGetVelocity;
                        msgGetVelocity.x = mainBody.LinearVelocity.X;
                        msgGetVelocity.y = mainBody.LinearVelocity.Y;
                        msgGetVelocity.z = 0.0f;
                    }
                    break;
                default:
                    return base.SendMessage(msg);
            }

            return true;

        }

        public override void Update(GameTime gameTime)
        {
            MsgSetPosition message1 = new MsgSetPosition(Parent.UniqueID, 
                ConvertUnits.ToDisplayUnits(mainBody.Position.X),
                ConvertUnits.ToDisplayUnits(mainBody.Position.Y), 0);
            Parent.Manager.SendMessage(message1);

            // Sending a message to the RenderComponent to change the rotation.
            // BREAKDOWN OF HOW THIS SPECIFIC MESSAGE WORK:
            //
            // 1) Create a (specific type of ) message, get the ID 
            //    of the entity you want to send it to and the info (rotation value) you wish to send.
            // 2) Send the message to the SCENEMANAGER. 
            // 3) The manager will cycle through each entity until it finds the matching ID.
            // 4) The Entity, through the poorly named SendMessage() method, will either handle the message,
            //    or pass on the message to each of its components.
            // 5) If a component can handle the message, (in this case RenderComponent), it will
            //    take the message, look in it's content(s), and do something with it.
            //    In this case, RenderComponent will make it's own rotation equal
            //    to the message's rotation value.
            // 6) If no Entity or Component ever handles the message, the SceneManager will say "FALSE",
            //    and nothing will happen. You just wasted some CPU cyces.

            MsgSetRotation message = new MsgSetRotation(Parent.UniqueID, mainBody.Rotation);
            Parent.Manager.SendMessage(message);
        }
    }
}
